package org.example.leetcode.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.leetcode.common._enum.DifficultyEnum;
import org.example.leetcode.common._enum.MailTypeEnum;
import org.example.leetcode.common._enum.RoleTypeEnum;
import org.example.leetcode.common._enum.YesOrNoEnum;
import org.example.leetcode.common.domain.SysUser;
import org.example.leetcode.common.exception.MyException;
import org.example.leetcode.common.utils.TokenUtils;
import org.example.leetcode.common.utils.UserInfo;
import org.example.leetcode.core.dao.SysUserMapper;
import org.example.leetcode.core.dao.TQuestionMapper;
import org.example.leetcode.core.domain.SysResource;
import org.example.leetcode.core.domain.SysRole;
import org.example.leetcode.core.domain.SysRoleResource;
import org.example.leetcode.core.dto.MenuResource;
import org.example.leetcode.core.dto.SysUserRegisterDto;
import org.example.leetcode.core.dto.SysUserTokenDto;
import org.example.leetcode.core.dto.UserInfoVo;
import org.example.leetcode.core.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.leetcode.core.service.impl.login.MailLogin;
import org.example.leetcode.core.utils.RedisUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhc
 * @since 2024-02-18
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService, ApplicationRunner {


    private final Map<String, LoginService> map=new HashMap<>();
    @Resource
    RedisUtils redisUtils;
    @Resource
    SysRoleResourceService sysRoleResourceService;
    @Resource
    SysResourceService sysResourceService;
    @Resource
    SysRoleService sysRoleService;
    @Resource
    TQuestionMapper tQuestionMapper;
    @Resource
    TaskExecutor taskExecutor;
    @Override
    public SysUserTokenDto loginOrRegister(String type, String phone, String password) {
        if(!phone.matches("\\d{11}")){
            throw new MyException("电话号码不规范");
        }
        if(!StringUtils.hasLength(password)){
            throw new MyException("请输入密码或验证码");
        }
        SysUserTokenDto sysUserTokenDto = map.get(type).loginOrRegister(phone, password);
        sysUserTokenDto.setPhone(phone);
        return sysUserTokenDto;
    }

    @Override
    public boolean checkFirstTimeLogin(String phone) {
        SysUser sysUser = this.getBaseMapper().selectOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone, phone));
        return sysUser==null||YesOrNoEnum.Yes.getCode().equals(sysUser.getIsNewUser());
    }

    @Override
    public String refreshToken(String phone) {
        String refreshToken =redisUtils.getValue("REFRESH_TOKEN:" + phone);
        if(!StringUtils.hasText(refreshToken)){
            return null;
        }
        //如果有refreshToken,重新创建双token，并返回accessToken
        JSONObject jsonObjectFromToken = TokenUtils.getJsonObjectFromToken(refreshToken);
        SysUser sysUser = JSONUtil.toBean(jsonObjectFromToken, SysUser.class);
        String newAccessToken = TokenUtils.createToken(sysUser.getId(), phone);
        String newRefreshToken = TokenUtils.createRefreshToken(sysUser.getId(), phone);
        redisUtils.setKey("REFRESH_TOKEN:"+phone,newRefreshToken,TokenUtils.TOKEN_VALID_TIME);
        redisUtils.setKey("ACCESS_TOKEN:"+phone,newAccessToken,TokenUtils.TOKEN_VALID_TIME*7);
        return newAccessToken;
    }

    @Override
    public void register(SysUserRegisterDto sysUserRegisterDto) {
        Assert.notEmpty(sysUserRegisterDto.getPhone(),"请填写信息");
        Assert.notEmpty(sysUserRegisterDto.getCode(),"请填写信息");
        Assert.notEmpty(sysUserRegisterDto.getPassword(),"请填写信息");
        Assert.notEmpty(sysUserRegisterDto.getPasswordConfirm(),"请填写信息");
        if(!sysUserRegisterDto.getPhone().matches("\\d{11}")){
            throw new MyException("电话号码不规范");
        }
        SysUser sysUser = this.getBaseMapper().selectOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone, sysUserRegisterDto.getPhone()));
        if(sysUser!=null){
            throw new MyException("手机号已注册");
        }
        if(!sysUserRegisterDto.getPassword().equals(sysUserRegisterDto.getPasswordConfirm())){
            throw new MyException("两次密码不统一");
        }
        if(MailLogin.MAIL_SWITCH){
            String correctCode=redisUtils.getValue(MailTypeEnum.Register.getName()+sysUserRegisterDto.getPhone());
            if(!StringUtils.hasText(correctCode)||!correctCode.equals(sysUserRegisterDto.getCode())){
                throw new MyException("验证码错误");
            }
        }
        SysUser newUser = new SysUser();
        newUser.setPhone(sysUserRegisterDto.getPhone());
        newUser.setPassword(sysUserRegisterDto.getPassword());
        newUser.setNickName("小卡拉米");
        newUser.setIsNewUser(YesOrNoEnum.No.getCode());
        this.save(newUser);
    }

    @Override
    public void setPassword(SysUserRegisterDto sysUserRegisterDto) {
        if(StringUtils.isEmpty(sysUserRegisterDto.getPassword())||StringUtils.isEmpty(sysUserRegisterDto.getPasswordConfirm())){
            throw new MyException("请输入密码");
        }
        SysUser sysUser = new SysUser();
        sysUser.setPassword(sysUserRegisterDto.getPassword());
        sysUser.setPhone(sysUserRegisterDto.getPhone());
        sysUser.setIsNewUser(YesOrNoEnum.No.getCode());
        this.baseMapper.update(sysUser,new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone,sysUserRegisterDto.getPhone()));
    }

    @Override
    public List<MenuResource> getResource(String phone, String type) {
        SysUser sysUser=null;
        if(StringUtils.hasText(phone)){
            sysUser = this.getBaseMapper().selectOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getPhone, phone));
            if(sysUser==null){
                throw new MyException("用户不存在");
            }
        }
        Long roleId ;
        //没传电话，说明是游客
        if(StringUtils.isEmpty(phone)){
            SysRole sysRole = sysRoleService.getBaseMapper().selectOne(new LambdaQueryWrapper<SysRole>()
                    .eq(SysRole::getRoleType, RoleTypeEnum.GUEST.getRoleCode()));
            roleId=sysRole.getId();
        }
        else{
            roleId=sysUser.getRoleId();
        }
        List<SysRoleResource> sysRoleResources = sysRoleResourceService.getBaseMapper().selectList(new LambdaQueryWrapper<SysRoleResource>()
                .eq(SysRoleResource::getRoleId, roleId));
        if(CollectionUtils.isEmpty(sysRoleResources)){
            return new ArrayList<>();
        }
        List<SysResource> sysResources = sysResourceService.getBaseMapper().selectList(new LambdaQueryWrapper<SysResource>()
                .in(SysResource::getId, sysRoleResources.stream().map(item->item.getResourceId()).collect(Collectors.toList()))
                .eq(SysResource::getType,type)
                .orderByAsc(SysResource::getSort));
        List<MenuResource> res=new ArrayList<>();
        sysResources.forEach(item->{
            MenuResource menuResource = new MenuResource();
            menuResource.setTitle(item.getName());
            menuResource.setKey(item.getCode());
            menuResource.setRoute(item.getRoute());
            res.add(menuResource);
        });
        return res;
    }

    @Override
    public Boolean logout() {
        redisUtils.removeKey("ACCESS_TOKEN:"+ UserInfo.getUserInfo().getPhone());
        return Boolean.TRUE;
    }

    @Override
    public UserInfoVo getUserInfo() {
        SysUser userInfo = UserInfo.getUserInfo();
        SysUser current = this.getById(userInfo.getId());
        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtil.copyProperties(current,userInfoVo);
        CompletableFuture completableFuture1=CompletableFuture.runAsync(()->userInfoVo.setEasyCount(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.EASY.getCode(), null)),taskExecutor);
        CompletableFuture completableFuture2=CompletableFuture.runAsync(()->userInfoVo.setEasyCorrect(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.EASY.getCode(), "Y")),taskExecutor);
        CompletableFuture completableFuture3=CompletableFuture.runAsync(()->userInfoVo.setMediumCount(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.MEDIUM.getCode(), null)),taskExecutor);
        CompletableFuture completableFuture4=CompletableFuture.runAsync(()->userInfoVo.setMediumCount(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.MEDIUM.getCode(), "Y")),taskExecutor);
        CompletableFuture completableFuture5=CompletableFuture.runAsync(()->userInfoVo.setHardCount(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.HARD.getCode(), null)),taskExecutor);
        CompletableFuture completableFuture6=CompletableFuture.runAsync(()->userInfoVo.setHardCount(calculateUserCorrectRateDetail(userInfoVo.getPhone(),DifficultyEnum.HARD.getCode(), "Y")),taskExecutor);
        CompletableFuture.allOf(completableFuture1,completableFuture2,completableFuture3,completableFuture4,completableFuture5,completableFuture6).join();
        return userInfoVo;
    }

    @Override
    public Boolean saveDetail(SysUser sysUser) {
        if(StringUtils.isEmpty(sysUser.getNickName())){
            throw new MyException("昵称不能为空");
        }
        this.baseMapper.update(sysUser,new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone,UserInfo.getUserInfo().getPhone()));
        return true;
    }

    private Long calculateUserCorrectRateDetail(String phone,String difficulty,String status){
        return tQuestionMapper.queryLogCount(phone, difficulty,status);
    }
    @Override
    public void run(ApplicationArguments args) throws Exception {
        Map<String,LoginService> loginServiceMap= SpringUtil.getBeansOfType(LoginService.class);
        for(Map.Entry<String,LoginService> entry:loginServiceMap.entrySet()){
            map.put(entry.getKey(),entry.getValue());
        }
    }
}
